"""
Copyright 2009, Jay Shurtz.

This is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this software; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.

Module for serial interface to physical implementation of a hatch design,
such as an FPGA using a serial port.

Example usage:

s = serial.Serial(DEFAULT_SERIAL_PORT, baudrate, bytesize, serial.PARITY_NONE, \
     serial.STOPBITS_ONE, DEFAULT_SERIAL_TIMEOUT, xonxoff=0, rtscts=0, dsrdtr=0)
    s.setRTS(1)
    s.setDTR(1)
    s.flushInput()
    s.flushOutput()

hi = HatchPortWrapperSerial(s, OpCodes={'read':0x21,'write':0x22}, byteOrder='>',\
      format='B', opCodeLen=1, addressLen=4, dataLen=4)

hi.read(node)
"""  

id = "$Id: hatch_serial.py 730 2010-09-30 06:17:05Z jayshurtz $"
# $URL: http://hatch.googlecode.com/svn/tags/taggle_release_3.0/hatch_interface/hatch_serial.py $
# $Author: jayshurtz $
version = " ".join(id.split()[1:3])


from os.path import basename

import to_hatch_structure
from hatch_port_wrapper import HatchPortWrapper, HatchPortError, ERROR_PORT
import serial   # __init__.py in the serial dir automatically handles different platform implementations of serial.


DEFAULT_SERIAL_PORT = '/dev/ttyUSB0'
DEFAULT_SERIAL_BAUDRATE = 115200
DEFAULT_SERIAL_TIMEOUT = 1
from os import name as osName
if (osName == 'nt'):    # Windows.
    DEFAULT_SERIAL_PORT = 'COM1'
del osName


class HatchPortWrapperSerial(HatchPortWrapper):
    """ Interface to the serial port of the hatched register implementation.
        This class re-raises SerialExceptions as the more generic 
        HatchPortError type, which is a specific subclass of IOError.
        Note that all calls to the logger have been commented out for speed.       
    """
    def __init__(self, port, logHandlers=[]):
        """ 'port' is an instance of the serial.Serial class which handles 
            things like timeouts & non-blocking reads. If a timeout is set, 
            reads may return less characters than requested. With no timeout, 
            reads will block until the requested number of bytes is read.
            'logHandlers' is a list of additional log handlers (such as 
            logging.FileHandler instances). .
        """
        self.portName = str(basename(port.getPort()))    # Ex: "/dev/ttyUSB0" -> "ttyUSB0".
        loggerName = self.__class__.__name__ + "_" + self.portName
        HatchPortWrapper.__init__(self, loggerName, logHandlers)
        self.port = port  # Class contains a regular serial port that it does things to.
        try:
            if self.port.getTimeout is None:
                self.port.setTimeout(self, DEFAULT_SERIAL_TIMEOUT)  # Force non-blocking reads with timeout.
        except serial.SerialException, e:   # Raise a more generic exception.
            raise HatchPortError, (ERROR_PORT, str(e))

    def open(self):
        """ Connect to and open the port instance.
            Do nothing if already open.
        """
        try:
            if not self.port.isOpen():
                self.port.open()
                self.port.flushInput()
                self.port.flushOutput()
        except serial.SerialException, e:   # Raise a more generic exception.
            raise HatchPortError, (ERROR_PORT, str(e))

    def close(self):
        """ Disconnect from and close the port instance.
            Do nothing if already closed.
        """
        try:
            if self.port.isOpen():
                self.port.flushInput()
                self.port.flushOutput()
                self.port.close()
        except serial.SerialException, e:   # Raise a more generic exception.
            raise HatchPortError, (ERROR_PORT, str(e))

    def inWaiting(self):
        """ Return the number of characters in the device port buffer.
        """
        try:
            return self.port.inWaiting()
        except serial.SerialException, e:   # Raise a more generic exception.
            raise HatchPortError, (ERROR_PORT, str(e))
        
    def flushOutput(self):
        """ Flush device output buffer.
        """
        try:
            self.port.flushOutput()
        except serial.SerialException, e:   # Raise a more generic exception.
            raise HatchPortError, (ERROR_PORT, str(e))
    
    def flushInput(self):
        """ Flush device input buffer.
        """
        try:
            self.port.flushInput()
        except serial.SerialException, e:   # Raise a more generic exception.
            raise HatchPortError, (ERROR_PORT, str(e))

    def write(self, value):
        """ Write to the port.
            'value' should be a string of packed binary values, for example:
            >>> write(struct.pack('>BB', 0x21, 0x22))   # ie: write('\x21\x22')
        """
        try:
            self.port.write(value)
        except serial.SerialException, e:   # Raise a more generic exception.
            raise HatchPortError, (ERROR_PORT, str(e))

    def read(self, length):
        """ Read from the port.
            'length' is the number of bytes to read.
            Returns as string of packed binary values, ex: '\x21\x22'
        """
        retVal = ""
        try:
            retVal = self.port.read(length)
        except serial.SerialException, e:   # Raise a more generic exception.
            raise HatchPortError, (ERROR_PORT, str(e))
        return retVal


class SerialDummy(object):
    """ Dummy serial.Serial class for debug & testing purposes only.
    """
    def setRTS(self, *args, **keyargs):
        pass
    def setDTR(self, *args, **keyargs):
        pass
    def close(self):
        pass
    def getPort(self):
        return "/dev/dummyPort"
    def getTimeout(self):
        return None
    def setTimeout(self, timeout):
        pass
    def isOpen(self):
        return False
    def open(self):
        pass
    def flushOutput(self):
        pass
    def flushInput(self):
        pass
    def inWaiting(self):
        return 0
    def write(self, data):
        pass
    def read(self, size=1):
        return "dummyRead"
    # XXX Add other functions here as needed.


# Main for testing.
if __name__ == "__main__":
    
  pass

